/*
===========================================================================
This source file is part of DyLab (Dynamic Laboratory)
For the latest info, see http://dylab-modules.googlecode.com

Copyright (c) 2006-2008 Lukas Krejci
(krejci.lukas@volny.cz)

This file is part of DyLab.

    DyLab is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    DyLab is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with DyLab.  If not, see <http://www.gnu.org/licenses/>.
===========================================================================
*/

//===========================================================================================
#include "dotTestT4Sub_ModelStructureLoader.h"
#include <dylab/simobject/masspoints/MassPointsStructureBase.h>
#include <dylab/simobject/tetravol/TetrahedralVolumeStructureBase.h>
#include <dylab/simobject/boundarymesh/BoundaryMeshStructureBase.h>
#include <dylab/utils/math/Math.h>
#include <dylab/utils/log/Log.h>

//===========================================================================================
const dylab::String dylab::dotTestT4Sub_ModelStructureLoader::COMPONENT_NAME = "test_t4sub";

//===========================================================================================
dylab::dotTestT4Sub_ModelStructureLoader::dotTestT4Sub_ModelStructureLoader()
: ComModelStructureLoader(COMPONENT_NAME)
{
}
//-----------------------------------------------------------------------------
void dylab::dotTestT4Sub_ModelStructureLoader::loadFromPropertyTree(const PropertyTree & _pt)
{ DYLAB_TRACE_CALLS
    // set node step value
    mNodeStep = 16;

    // first, generate testing structure
    _generateModel();

    // test: edge subdivision
    _testEdgeSubdivision(3449012426);

    // test: cull out the tetrahedra
    _testCullTetrahedra(652941186);    

    // test: subdivide tetrahedron
    _testTetrahedronSubdivision(1209392067);

    // test: cull out the tetrahedra
    _testCullTetrahedra(1554111547);    

    // finally, generate boundary mesh for rendering
    _generateBoundaryMesh();
 }
//-----------------------------------------------------------------------------
void dylab::dotTestT4Sub_ModelStructureLoader::_generateModel()
{ DYLAB_TRACE_CALLS

    // first, generate testing box
    _generateBox(Vector3(-8, 32, -8), 16, 16, 16);
 }
//-----------------------------------------------------------------------------
void dylab::dotTestT4Sub_ModelStructureLoader::_generateBox(const Vector3 & _start, uint32_t _width, uint32_t _height, uint32_t _depth)
{ DYLAB_TRACE_CALLS

    int32_t nodeCount, cubeCount, i;

    // compute number of nodes in each direction
    int32_t xNodes = _width / mNodeStep + 1;
    int32_t yNodes = _height / mNodeStep + 1;
    int32_t zNodes = _depth / mNodeStep + 1;

    // store the number of all box nodes and cube count (each cube is subdivided into 5 tetrahedrons)
    nodeCount = xNodes * yNodes * zNodes;
    cubeCount = (xNodes - 1) * (yNodes - 1) * (zNodes - 1);

    // retrieve mass points interface
	IMassPointsStructure * massPoints = MassPointsStructureBase::getImplFrom(getModelToLoad());
    MassPoint * mp;

    // create mass points
    MassPoint::id_t firstMP = massPoints->createMassPoints(nodeCount);

    // first pass is going through all box nodes initializing its attributes based on supplied parameters
    for (int32_t y = 0; y < yNodes; y++) 
        for (int32_t z = 0; z < zNodes; z++) 
            for (int32_t x = 0; x < xNodes; x++) 
    {
        // compute node index based on its position within the box
        i = x + z * xNodes + y * (zNodes * xNodes);

        // create new mass point
        mp = massPoints->massPointReader().get(firstMP + i);

        // set mass point position in the box
        Vector3 pos = Vector3(x, y, z).mul((real_t)mNodeStep) + _start;
        mp->currState().position = pos;

        // set mass point texture coordinates based on mass point position in terms of the box - TODO: implement better texture mapping
        // TODO
        //mp->setU(float(x + z * xNodes) / float(xNodes * zNodes)).setV(float(y) / float(yNodes - 1));

        // set boundary flag
        if ((x == 0) || (x == xNodes - 1) || (y == 0) || (y == yNodes - 1) || (z == 0) || (z == zNodes - 1))
            mp->flags().set(MassPoint::F_BOUNDARY);
    }

    // utility variables
    int32_t n, firstNode, xzNodes;
    xzNodes = xNodes * zNodes;
    bool rotateCube = true;

    // retrieve tetrahedral volume interface
	ITetrahedralVolumeStructure * tetraVol = TetrahedralVolumeStructureBase::getImplFrom(getModelToLoad());

    // second pass goes through indiviadual cubes creating 5 tetrahedrons for each one of them
    for (i = 0, n = 1; i < cubeCount; i++, n++)
    {
        if ((n % xNodes) == 0) { // last node of the row => new row (z++)
            n++;
            if ((xNodes % 2) == 1) // odd number of xNodes => extra switch of the rotateCube flag when entering new row
                rotateCube = !rotateCube;
        }
        if (((n + xNodes - 1) % xzNodes) == 0) // first node on the last row => new level (y++)
            n += xNodes;

        // index of the cube first node (upper-left node at the bottom plane)
        firstNode = n - 1;

        if (rotateCube) // this tetrahedron box will be rotated around axis y about 90 degrees
            tetraVol->generateCubeSection(firstNode, firstNode + 1, firstNode + 1 + xNodes, firstNode + xNodes, 
                            xzNodes + firstNode, xzNodes + firstNode + 1, xzNodes + firstNode + 1 + xNodes, xzNodes + firstNode + xNodes);
        else
            tetraVol->generateCubeSection(firstNode + 1, firstNode + 1 + xNodes, firstNode + xNodes, firstNode,
                            xzNodes + firstNode + 1, xzNodes + firstNode + 1 + xNodes, xzNodes + firstNode + xNodes, xzNodes + firstNode);
 
        // swap the the cube rotation flag
        rotateCube = !rotateCube;
    }
 }
//-----------------------------------------------------------------------------
void dylab::dotTestT4Sub_ModelStructureLoader::_generateBoundaryMesh()
{ DYLAB_TRACE_CALLS

    // generate boundary mesh
	IBoundaryMeshStructure * boundaryMesh = BoundaryMeshStructureBase::getImplFrom(getModelToLoad());
    boundaryMesh->generateFromTetrahedralVolume();

    // TODO: set (generate?) the uv-texture mapping for each boundary vertex
    for (VertexContainer::Iterator vertex(boundaryMesh->vertexIterator()); vertex.isValid(); vertex++)
    {
        vertex->setTextureU(0);
        vertex->setTextureV(1);
    }
 }
//-----------------------------------------------------------------------------
void dylab::dotTestT4Sub_ModelStructureLoader::_testEdgeSubdivision(Edge::id_t _edgeID)
{ DYLAB_TRACE_CALLS

    // retrieve mass points & tetrahedral volume interface
	IMassPointsStructure * massPoints = MassPointsStructureBase::getImplFrom(getModelToLoad());
    ITetrahedralVolumeStructure * tetraVol = TetrahedralVolumeStructureBase::getImplFrom(getModelToLoad());

    // for log info
    uint32_t subdividedEdgeCount = 0;

    // walk through all structure edges
    for (EdgeContainer::Iterator edge(tetraVol->edgeIterator()); edge.isValid(); )
    {
        // retrieve edge geometry
        EdgeGeometry3 edgeGeom = edge->getGeometry();

        // log info about the current edge
        DYLAB_LOGF2("Edge: id = %1%, geometry = %2%", ~edge, edgeGeom);

        // chose one edge (id is taken from the log)
        if (~edge == _edgeID)
        {
            // get center of the edge geometry
            Vector3 p = edgeGeom.computeCenter();

            // log info about the subdivision point
            DYLAB_LOGF("\tThis edge is to be subdivided at %1% ...", p);

            // increment edge walker (we need to do this before the subdivision process, because after it, the walker is no longer valid
            edge++;

            // perform the subdivision
            tetraVol->subdivideEdge(_edgeID, p);

            // incerement counter
            subdividedEdgeCount++;
        }
        else
        {
            // increment edge walker
            edge++;
        }
    }

    // log subdivision info
    DYLAB_LOGF("Subdivided edges total: %1%", subdividedEdgeCount);
 }
//-----------------------------------------------------------------------------
void dylab::dotTestT4Sub_ModelStructureLoader::_testTetrahedronSubdivision(Tetrahedron::id_t _tetraID)
{ DYLAB_TRACE_CALLS

    // retrieve mass points & tetrahedral volume interface
    IMassPointsStructure * massPoints = MassPointsStructureBase::getImplFrom(getModelToLoad());
    ITetrahedralVolumeStructure * tetraVol = TetrahedralVolumeStructureBase::getImplFrom(getModelToLoad());

    // for log info
    uint32_t subdividedTetraCount = 0;

    // walk through all structure tetras
    for (TetrahedronContainer::Iterator tetra(tetraVol->tetrahedronIterator()); tetra.isValid(); )
    {
        // retrieve tetra geometry
        TetrahedronGeometry tetraGeom = tetra->getGeometry();

        // log info about the current tetra
        DYLAB_LOGF2("Tetra: id = %1%, geometry = %2%", ~tetra, tetraGeom);

        // chose one tetra (id is taken from the log)
        if (~tetra == _tetraID)
        {
            Vector3 p = tetraGeom.computeCenter();

            // log info about the subdivision point
            DYLAB_LOGF("\tThis tetra is to be subdivided at point %1% ...", p);

            // increment tetra walker (we need to do this before the subdivision process, because after it, the walker is no longer valid
            tetra++;

            // perform the subdivision
            tetraVol->subdivideTetrahedron(_tetraID, p);

            // incerement counter
            subdividedTetraCount++;
        }
        else
        {
            // increment tetra walker
            tetra++;
        }
    }

    // log subdivision info
    DYLAB_LOGF("Subdivided tetras total: %1%", subdividedTetraCount);
 }
//-----------------------------------------------------------------------------
void dylab::dotTestT4Sub_ModelStructureLoader::_testCullTetrahedra(Tetrahedron::id_t _tetraID)
{ DYLAB_TRACE_CALLS

    // retrieve mass points & tetrahedral volume interface
    IMassPointsStructure * massPoints = MassPointsStructureBase::getImplFrom(getModelToLoad());
    ITetrahedralVolumeStructure * tetraVol = TetrahedralVolumeStructureBase::getImplFrom(getModelToLoad());

    // for log info
    uint32_t culledTetraCount = 0;

    // walk through all structure tetras
    for (TetrahedronContainer::Iterator tetra(tetraVol->tetrahedronIterator()); tetra.isValid(); )
    {
        // retrieve tetra geometry
        TetrahedronGeometry tetraGeom = tetra->getGeometry();

        // log info about the current tetra
        DYLAB_LOGF2("Tetra: id = %1%, geometry = %2%", ~tetra, tetraGeom);

        // chose one tetra (id is taken from the log)
        if (~tetra == _tetraID)
        {
            // log info about the subdivision point
            DYLAB_LOG("\tThis tetra is to be culled out ...");

            // increment tetra walker (we need to do this before the subdivision process, because after it, the walker is no longer valid
            tetra++;

            // perform the subdivision
            tetraVol->destroyTetrahedron(_tetraID);

            // incerement counter
            culledTetraCount++;
        }
        else
        {
            // increment tetra walker
            tetra++;
        }
    }

    // log subdivision info
    DYLAB_LOGF("Culled tetras total: %1%", culledTetraCount);
 }
//-----------------------------------------------------------------------------